#pragma once

#include <iostream>
#include <vector>
#include "Sem.hpp"
#include "Mutex.hpp"

static const int gcap = 5;

using namespace SemModule;
using namespace MutexModule;

template<typename T>
class RingQueue
{
public:
    RingQueue(int cap = gcap)
        : _cap(cap),
          _rq(cap),
          _blank_sem(cap),
          _p_step(0),
          _data_sem(0),
          _c_step(0)
    {}

    void Push(const T& in)
    {
        // 1. 申请空位置信号量
        _blank_sem.P();
        {
            LockGuard lockguard(_pmutex);
            // 2. 生产 -- 在_p_step位置进行生产
            _rq[_p_step] = in;
            // 3. 更新下标
            ++_p_step;
            // 4. 维持环形特性
            _p_step %= _cap;
        }
        // 5. 生产完数据之后，将数据信号量++
        _data_sem.V();
    }

    void Pop(T* out)
    {
        // 1. 申请数据信号量
        _data_sem.P();  // 如果还没有生产者进行Push操作执行V操作，则_data_sem信号量中没有可用信号量，则会阻塞在这
        {
            LockGuard lockguard(_cmutex);
            // 2. 消费 -- 取出_c_step位置的数据
            *out = _rq[_c_step];
            // 3. 更新下标
            ++_c_step;
            // 4. 维持环形特性
            _c_step %= _cap;
        }
        // 5. 消费完数据之后，将空位置信号量++
        _blank_sem.V();
    }
    ~RingQueue()
    {}
private:
    std::vector<T> _rq;
    int _cap;

    // 生产者
    Sem _blank_sem;
    int _p_step;    // 生产者环形队列中的下标

    // 消费者
    Sem _data_sem;
    int _c_step;    // 消费者环形队列中的下标

    Mutex _pmutex;  // 维护多生产者之间的互斥关系
    Mutex _cmutex;  // 维护消费者之间的互斥关系
};